/*******************************************************************************
* Copyright (c) 2007 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.seam.internal.core.scanner.java;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.jboss.tools.common.model.project.ext.IValueInfo;
import org.jboss.tools.common.model.project.ext.impl.ValueInfo;
import org.jboss.tools.seam.core.BeanType;
import org.jboss.tools.seam.core.BijectedAttributeType;
import org.jboss.tools.seam.core.ISeamXmlComponentDeclaration;
import org.jboss.tools.seam.core.SeamComponentMethodType;
import org.jboss.tools.seam.core.SeamCorePlugin;
import org.jboss.tools.seam.internal.core.BijectedAttribute;
import org.jboss.tools.seam.internal.core.DataModelSelectionAttribute;
import org.jboss.tools.seam.internal.core.Role;
import org.jboss.tools.seam.internal.core.SeamAnnotatedFactory;
import org.jboss.tools.seam.internal.core.SeamComponentMethod;
import org.jboss.tools.seam.internal.core.SeamJavaComponentDeclaration;
import org.jboss.tools.seam.internal.core.scanner.LoadedDeclarations;
/**
* Builds component using results of ASTVisitorImpl
*
* @author Viacheslav Kabanovich
*/
public class ComponentBuilder implements SeamAnnotations {
LoadedDeclarations ds = null;
AnnotatedASTNode<?> annotatedType = null;
Set<AnnotatedASTNode<FieldDeclaration>> annotatedFields = null;
Set<AnnotatedASTNode<MethodDeclaration>> annotatedMethods = null;
SeamJavaComponentDeclaration component = new SeamJavaComponentDeclaration();
public ComponentBuilder(LoadedDeclarations ds, ASTVisitorImpl.TypeData visitor) {
this.ds = ds;
annotatedType = visitor.annotatedType;
annotatedFields = visitor.annotatedFields;
annotatedMethods = visitor.annotatedMethods;
String n = visitor.type.getElementName();
n = JavaScanner.getResolvedType(visitor.type, n);
ds.getComponents().add(component);
component.setType(visitor.type);
component.setId(visitor.type);
component.setClassName(n);
}
void process() {
if(annotatedType == null) return;
ResolvedAnnotation[] as = annotatedType.getAnnotations();
if(as != null) for (int i = 0; i < as.length; i++) {
String type = as[i].getType();
if(NAME_ANNOTATION_TYPE.equals(type)) {
component.setName(ValueInfo.getValueInfo(as[i].getAnnotation(), null));
} else if(SCOPE_ANNOTATION_TYPE.equals(type)) {
ValueInfo scope = ValueInfo.getValueInfo(as[i].getAnnotation(), null);
if(scope != null && scope.getValue() != null) {
int q = scope.getValue().lastIndexOf('.');
if(q >= 0) scope.setValue(scope.getValue().substring(q + 1).toLowerCase());
}
component.setScope(scope);
} else if(INSTALL_ANNOTATION_TYPE.equals(type)) {
component.setPrecedence(ValueInfo.getValueInfo(as[i].getAnnotation(), "precedence")); //$NON-NLS-1$
} else if(IMPORT_ANNOTATION_TYPE.equals(type)) {
processImport(as[i].getAnnotation());
}
}
if(as != null) {
Map<BeanType, IValueInfo> types = new HashMap<BeanType, IValueInfo>();
for (int i = 0; i < BeanType.values().length; i++) {
Annotation a = findAnnotation(annotatedType, BeanType.values()[i].getAnnotationType());
if(a != null) {
ValueInfo v = new ValueInfo();
v.setValue("true"); //$NON-NLS-1$
v.valueStartPosition = a.getStartPosition();
v.valueLength = a.getLength();
types.put(BeanType.values()[i], v);
}
}
if(!types.isEmpty()) {
component.setTypes(types);
}
}
processFactories();
processBijections();
processComponentMethods();
processRoles();
}
void processFactories() {
for (AnnotatedASTNode<MethodDeclaration> n: annotatedMethods) {
Annotation a = findAnnotation(n, FACTORY_ANNOTATION_TYPE);
if(a == null) continue;
MethodDeclaration m = n.getNode();
ValueInfo factoryName = ValueInfo.getValueInfo(a, null);
if(factoryName == null) {
factoryName = new ValueInfo();
factoryName.setValue(toPropertyName(m.getName().getIdentifier(), "get"));
factoryName.valueLength = m.getName().getLength();
factoryName.valueStartPosition = m.getName().getStartPosition();
}
ValueInfo scope = ValueInfo.getValueInfo(a, ISeamXmlComponentDeclaration.SCOPE);
ValueInfo autoCreate = ValueInfo.getValueInfo(a, "autoCreate"); //$NON-NLS-1$
SeamAnnotatedFactory factory = new SeamAnnotatedFactory();
factory.setParentDeclaration(component);
IMethod im = findMethod(m);
factory.setSourceMember(im);
factory.setId(im);
factory.setSourcePath(component.getSourcePath());
factory.setName(factoryName);
if(autoCreate != null) factory.setAutoCreate(true);
if(scope != null) {
factory.setScope(scope);
}
ValueInfo _a = new ValueInfo();
_a.setValue(FACTORY_ANNOTATION_TYPE);
_a.valueStartPosition = a.getStartPosition();
_a.valueLength = a.getLength();
factory.addAttribute(FACTORY_ANNOTATION_TYPE, _a);
ds.getFactories().add(factory);
}
}
private String toPropertyName(String methodName, String prefix) {
if(methodName == null) {
return methodName;
}
if(methodName.startsWith(prefix) && methodName.length() > prefix.length()) {
String root = methodName.substring(prefix.length());
return root.substring(0, 1).toLowerCase() + root.substring(1);
}
return methodName;
}
void processBijections() {
Map<BijectedAttributeType, Annotation> as = new HashMap<BijectedAttributeType, Annotation>();
List<BijectedAttributeType> types = new ArrayList<BijectedAttributeType>();
for (AnnotatedASTNode<MethodDeclaration> n: annotatedMethods) {
getBijectedType(n, as, types);
if(as.isEmpty()) continue;
boolean isDataModelSelectionType = !types.get(0).isUsingMemberName();
MethodDeclaration m = n.getNode();
BijectedAttribute att = createBijectedAttribute(types);
Annotation in = as.get(BijectedAttributeType.IN);
Annotation out = as.get(BijectedAttributeType.OUT);
Annotation data = as.get(BijectedAttributeType.DATA_BINDER);
addLocation(att, in, IN_ANNOTATION_TYPE);
addLocation(att, out, OUT_ANNOTATION_TYPE);
addLocation(att, data, DATA_MODEL_ANNOTATION_TYPE);
ValueInfo name = findValueInfo(as, null);
att.setValue(name);
if(name == null || isDataModelSelectionType
|| name.getValue() == null || name.getValue().length() == 0) {
name = new ValueInfo();
name.valueStartPosition = m.getStartPosition();
name.valueLength = m.getLength();
name.setValue(m.getName().getIdentifier());
if(in != null) {
name.setValue(toPropertyName(name.getValue(), "set"));
} else if(out != null || data != null) {
name.setValue(toPropertyName(name.getValue(), "get"));
}
}
att.setName(name);
ValueInfo scope = findValueInfo(as, "scope"); //$NON-NLS-1$
if(scope != null) att.setScope(scope);
IMethod im = findMethod(m);
att.setSourceMember(im);
att.setId(im);
}
for (AnnotatedASTNode<FieldDeclaration> n: annotatedFields) {
getBijectedType(n, as, types);
if(as.isEmpty()) continue;
boolean isDataModelSelectionType = !types.get(0).isUsingMemberName();
FieldDeclaration m = n.getNode();
BijectedAttribute att = createBijectedAttribute(types);
addLocation(att, as.get(BijectedAttributeType.IN), IN_ANNOTATION_TYPE);
addLocation(att, as.get(BijectedAttributeType.OUT), OUT_ANNOTATION_TYPE);
addLocation(att, as.get(BijectedAttributeType.DATA_BINDER), DATA_MODEL_ANNOTATION_TYPE);
ValueInfo name = findValueInfo(as, null);
att.setValue(name);
if(name == null || isDataModelSelectionType
|| name.getValue() == null || name.getValue().length() == 0) {
name = new ValueInfo();
name.valueStartPosition = m.getStartPosition();
name.valueLength = m.getLength();
name.setValue(getFieldName(m));
}
att.setName(name);
ValueInfo scope = findValueInfo(as, "scope"); //$NON-NLS-1$
if(scope != null) att.setScope(scope);
IField f = findField(m);
att.setSourceMember(f);
att.setId(f);
}
}
private void getBijectedType(AnnotatedASTNode<?> n,
Map<BijectedAttributeType, Annotation> as, List<BijectedAttributeType> types) {
as.clear();
types.clear();
for (int i = 0; i < BijectedAttributeType.values().length; i++) {
Annotation a = findAnnotation(n, BijectedAttributeType.values()[i].getAnnotationType());
if(a != null) {
as.put(BijectedAttributeType.values()[i], a);
types.add(BijectedAttributeType.values()[i]);
}
}
}
private ValueInfo findValueInfo(Map<BijectedAttributeType, Annotation> as, String name) {
for (BijectedAttributeType t: BijectedAttributeType.values()) {
Annotation a = as.get(t);
if(a == null) continue;
ValueInfo result = ValueInfo.getValueInfo(a, name);
if(result != null && result.getValue() != null && result.getValue().length() > 0) return result;
}
return null;
}
private BijectedAttribute createBijectedAttribute(List<BijectedAttributeType> types) {
boolean isDataModelSelectionType = !types.get(0).isUsingMemberName();
BijectedAttribute att = (!isDataModelSelectionType)
? new BijectedAttribute() : new DataModelSelectionAttribute();
component.addBijectedAttribute(att);
att.setTypes(types.toArray(new BijectedAttributeType[0]));
return att;
}
private void addLocation(BijectedAttribute att, Annotation a, String name) {
if(a != null) {
ValueInfo _a = new ValueInfo();
_a.setValue(name);
_a.valueStartPosition = a.getStartPosition();
_a.valueLength = a.getLength();
att.addAttribute(name, _a);
}
}
void processComponentMethods() {
for (AnnotatedASTNode<MethodDeclaration> n: annotatedMethods) {
SeamComponentMethod cm = null;
for (int i = 0; i < SeamComponentMethodType.values().length; i++) {
SeamComponentMethodType type = SeamComponentMethodType.values()[i];
Annotation a = findAnnotation(n, type.getAnnotationType());
if(a == null) continue;
if(cm == null) {
cm = new SeamComponentMethod();
component.addMethod(cm);
MethodDeclaration m = n.getNode();
IMethod im = findMethod(m);
cm.setSourceMember(im);
cm.setId(im);
}
cm.getTypes().add(type);
}
}
}
void processRoles() {
Annotation roles = findAnnotation(annotatedType, ROLES_ANNOTATION_TYPE);
if(roles != null) {
RolesVisitor visitor = new RolesVisitor((IType)component.getSourceMember());
roles.accept(visitor);
List<Annotation> rs = visitor.annotations;
if(rs != null) for (Annotation role : rs) {
createRole(role);
}
}
ResolvedAnnotation[] as = annotatedType.getAnnotations();
if(as != null) for (int i = 0; i < as.length; i++) {
if(ROLE_ANNOTATION_TYPE.equals(as[i].getType())) {
createRole(as[i].getAnnotation());
}
}
}
void createRole(Annotation role) {
Role r = new Role();
r.setSourcePath(component.getSourcePath());
r.setSourceMember(component.getSourceMember());
ValueInfo name = ValueInfo.getValueInfo(role, "name"); //$NON-NLS-1$
if(name == null) return;
r.setId("" + component.getName() + ":" + name.getValue()); //$NON-NLS-1$ //$NON-NLS-2$
r.setName(name);
ValueInfo scope = ValueInfo.getValueInfo(role, "scope"); //$NON-NLS-1$
if(scope != null) r.setScope(scope);
component.addRole(r);
}
private void processImport(Annotation a) {
List<String> vs = getArrayValue(a);
for (String v: vs) component.addImport(v);
}
private Annotation findAnnotation(AnnotatedASTNode<?> n, String type) {
ResolvedAnnotation[] as = n.getAnnotations();
if(as == null) return null;
for (int i = 0; i < as.length; i++) {
if(type.equals(as[i].getType())) return as[i].getAnnotation();
}
return null;
}
private IMethod findMethod(MethodDeclaration m) {
if(m == null || m.getName() == null) return null;
IType type = (IType)component.getSourceMember();
IMethod[] ms = null;
try {
ms = type.getMethods();
} catch (JavaModelException e) {
SeamCorePlugin.getDefault().logError(e);
}
String name = m.getName().getIdentifier();
if(ms != null) for (int i = 0; i < ms.length; i++) {
if(!name.equals(ms[i].getElementName())) continue;
int s = m.getStartPosition() + m.getLength() / 2;
try {
ISourceRange range = ms[i].getSourceRange();
if(range == null) {
//no source and we cannot check position.
return ms[i];
}
int b = range.getOffset();
int e = b + range.getLength();
if(s >= b && s <= e) return ms[i];
} catch (JavaModelException e) {
return ms[i];
}
}
return null;
}
private IField findField(FieldDeclaration f) {
if(f == null || getFieldName(f) == null) return null;
IType type = (IType)component.getSourceMember();
IField[] fs = null;
try {
fs = type.getFields();
} catch (JavaModelException e1) {
SeamCorePlugin.getDefault().logError(e1);
}
String name = getFieldName(f);
if(fs != null) for (int i = 0; i < fs.length; i++) {
if(!name.equals(fs[i].getElementName())) continue;
int s = f.getStartPosition() + f.getLength() / 2;
try {
ISourceRange range = fs[i].getSourceRange();
if(range == null) {
//no source and we cannot check position.
return fs[i];
}
int b = range.getOffset();
int e = b + range.getLength();
if(s >= b && s <= e) return fs[i];
} catch (JavaModelException e) {
return fs[i];
}
}
return null;
}
/**
* Returns name of first field
* @param node
* @return
*/
String getFieldName(FieldDeclaration node) {
List<?> fragments = node.fragments();
for (int i = 0; i < fragments.size(); i++) {
VariableDeclaration vd = (VariableDeclaration)fragments.get(i);
String name = vd.getName().getIdentifier();
return name;
}
return null;
}
static List<String> getArrayValue(Annotation a) {
List<String> result = new ArrayList<String>();
if(a instanceof SingleMemberAnnotation) {
SingleMemberAnnotation s = (SingleMemberAnnotation)a;
Expression exp = s.getValue();
if(exp instanceof StringLiteral) {
result.add(((StringLiteral)exp).getLiteralValue());
} else if(exp instanceof QualifiedName) {
Object o = exp.resolveConstantExpressionValue();
if(o != null) result.add(o.toString());
result.add(exp.toString());
} else if(exp instanceof ArrayInitializer) {
ArrayInitializer arr = (ArrayInitializer)exp;
List<?> es = arr.expressions();
if(es != null) for (Object e: es) {
Expression exp1 = (Expression)e;
if(exp1 instanceof StringLiteral) {
result.add(((StringLiteral)exp1).getLiteralValue());
}
}
}
}
return result;
}
}
class RolesVisitor extends ASTVisitor implements SeamAnnotations {
boolean arrayFound = false;
IType type;
List<Annotation> annotations = new ArrayList<Annotation>();
public RolesVisitor(IType type) {
this.type = type;
}
public boolean visit(SingleMemberAnnotation node) {
return true;
}
public boolean visit(NormalAnnotation node) {
String typeName = ASTVisitorImpl.resolveType(type, node);
if(ROLE_ANNOTATION_TYPE.equals(typeName)) {
annotations.add(node);
return true;
}
return false;
}
public boolean visit(ArrayInitializer node) {
return true;
}
}